package com.hsy.crm.web.service.asyn;

import com.google.common.collect.Lists;
import com.hsy.crm.web.domain.entity.Account;
import com.hsy.crm.web.domain.entity.AccountDetail;
import com.hsy.crm.web.domain.entity.Brand;
import com.hsy.crm.web.domain.entity.Company;
import com.hsy.crm.web.domain.entity.Customer;
import com.hsy.crm.web.domain.entity.CustomerType;
import com.hsy.crm.web.domain.entity.Data;
import com.hsy.crm.web.domain.entity.Dict;
import com.hsy.crm.web.domain.entity.FinOtherOrder;
import com.hsy.crm.web.domain.entity.FinOtherOrderAccount;
import com.hsy.crm.web.domain.entity.FinTransOrder;
import com.hsy.crm.web.domain.entity.FinTransOrderDetail;
import com.hsy.crm.web.domain.entity.Menu;
import com.hsy.crm.web.domain.entity.Order;
import com.hsy.crm.web.domain.entity.OrderAccount;
import com.hsy.crm.web.domain.entity.OrderDetail;
import com.hsy.crm.web.domain.entity.OrderResubject;
import com.hsy.crm.web.domain.entity.PlatformCustomer;
import com.hsy.crm.web.domain.entity.Print;
import com.hsy.crm.web.domain.entity.Product;
import com.hsy.crm.web.domain.entity.ProductBatch;
import com.hsy.crm.web.domain.entity.ProductMultiUnit;
import com.hsy.crm.web.domain.entity.ProductPrice;
import com.hsy.crm.web.domain.entity.ProductType;
import com.hsy.crm.web.domain.entity.ProfitReport;
import com.hsy.crm.web.domain.entity.ProfitReportDetail;
import com.hsy.crm.web.domain.entity.ReceiptOrder;
import com.hsy.crm.web.domain.entity.ReceiptOrderAccount;
import com.hsy.crm.web.domain.entity.ReceiptOrderOrder;
import com.hsy.crm.web.domain.entity.Resubject;
import com.hsy.crm.web.domain.entity.Role;
import com.hsy.crm.web.domain.entity.RoleMenu;
import com.hsy.crm.web.domain.entity.SaleCostDetail;
import com.hsy.crm.web.domain.entity.Schema;
import com.hsy.crm.web.domain.entity.SplitProduct;
import com.hsy.crm.web.domain.entity.SplitProductDetail;
import com.hsy.crm.web.domain.entity.StoneOther;
import com.hsy.crm.web.domain.entity.StoneOtherDetail;
import com.hsy.crm.web.domain.entity.Store;
import com.hsy.crm.web.domain.entity.StoreCheck;
import com.hsy.crm.web.domain.entity.StoreCheckDetail;
import com.hsy.crm.web.domain.entity.StoreProduct;
import com.hsy.crm.web.domain.entity.StoreProductDetail;
import com.hsy.crm.web.domain.entity.TransferOrder;
import com.hsy.crm.web.domain.entity.TransferOrderDetail;
import com.hsy.crm.web.domain.entity.Unit;
import com.hsy.crm.web.domain.entity.User;
import com.hsy.crm.web.domain.entity.UserRole;
import com.hsy.crm.web.domain.entity.Vendor;
import com.hsy.crm.web.domain.entity.VendorType;
import com.hsy.crm.web.domain.entity.VerificateOrder;
import com.hsy.crm.web.domain.entity.VerificateOrderDetail;
import com.hsy.crm.web.domain.vo.platform.SchemaVo;
import com.hsy.crm.web.mapper.CompanyMapper;
import com.hsy.crm.web.mapper.SchemaMapper;
import com.hsy.crm.web.mapper.StoneMapper;
import com.hsy.crm.web.utils.ServiceUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Column;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.List;

@Service
public class SchemaService {

    public static final int init_type = 1;

    Logger logger = LoggerFactory.getLogger(SchemaService.class);

    @Autowired
    SchemaMapper schemaMapper;

    @Autowired
    CompanyMapper companyMapper;

    @Autowired
    StoneMapper stoneMapper;

    /**
     * 添加需要添加ALTER TABLE   customerId
     */
    private static final String[] PLAT_TABLES = {"sys_schema","sys_dict","sys_menu","sys_platform_customer","sys_print","sys_data","sys_user"};

    // EntiytsTest.main
    private static final Class<?>[] entityClass = {Account.class, AccountDetail.class,
            Brand.class, Company.class, Customer.class, CustomerType.class, Dict.class, FinOtherOrder.class,
            FinOtherOrderAccount.class, Menu.class, Order.class, OrderAccount.class,
            OrderDetail.class, OrderResubject.class, PlatformCustomer.class,
            Print.class, Product.class, ProductMultiUnit.class, ProductPrice.class, ProductType.class,
            ProfitReport.class, ProfitReportDetail.class, ReceiptOrder.class, ReceiptOrderAccount.class,
            ReceiptOrderOrder.class, Resubject.class, Role.class, RoleMenu.class, Schema.class, SplitProduct.class,
            SplitProductDetail.class, StoneOther.class, StoneOtherDetail.class, Store.class,
            StoreCheck.class, StoreCheckDetail.class, StoreProduct.class, StoreProductDetail.class,
            TransferOrder.class, TransferOrderDetail.class, Unit.class, User.class,
            UserRole.class, Vendor.class, VendorType.class,FinTransOrder.class,FinTransOrderDetail.class,
            VerificateOrderDetail.class,VerificateOrder.class,SaleCostDetail.class,Data.class,ProductBatch.class};


    public void init(String customerId,int type){
        for( Class<?> clazz : entityClass ){
            String tableName = clazz.getAnnotation(Table.class).name();
            Field[] fields =  FieldUtils.getAllFields(clazz);

            if( init_type != type ){
                table(tableName,fields,customerId,type);
            }else{
                if( !isPlatTable(tableName) ){
                    table(tableName,fields,customerId,type);
                }
            }
        }
    }

    /**
     * 默认公司
     * 默认账户
     * 用户初始化
     * 用户权限
     * 默认仓库
     * 打印功能
     */
    @Transactional
    public void init_data(SchemaVo schemaVo){
        String customerId = schemaVo.getCustomerId();
        for( Class<?> clazz : entityClass ){
            String tableName = clazz.getAnnotation(Table.class).name();

            if( !isPlatTable(tableName) ){
                clearTable(tableName,customerId);
            }
        }



        // init data start
        init_style_data(schemaVo);

    }

    public void init_style_data(SchemaVo schemaVo){
        String customerId = schemaVo.getCustomerId();
        PlatformCustomer platformCustomer = new PlatformCustomer();
        platformCustomer.setPlatCustomerId(customerId);
        platformCustomer = schemaMapper.findPlatformCustomer(platformCustomer);

        //company
        Company company = new Company();
        company.setCode("000");
        company.setName(platformCustomer.getCompanyName());
        company.setLogicalCode("100");
        company.setParentId(-1L);
        company.setContact(platformCustomer.getContact());
        company.setContactPhone(platformCustomer.getContactPhone());
        company.setLoginUserId(schemaVo.getLoginUserId());
        company.setCustomerId(customerId);
        ServiceUtils.init_entity_insert(company);
        companyMapper.insert(company);

        //plat customer created user  query by phone
        User user = new User();
        user.setCustomerId(customerId);
        user.setPhone(platformCustomer.getContactPhone());
        user = schemaMapper.findPlatUser(user);

        //user role
        List<String> sqls = Lists.newArrayList();
        sqls.add("INSERT INTO `sys_role_"+customerId+"`(`id`,company_id, `name`,comments, `create_user_id`, `create_time`, `update_user_id`, `update_time`) VALUES (400000001,"+company.getId()+", '管理员','管理员', 0, -1, 0, -1)");
        sqls.add("insert into sys_role_menu_"+customerId+"(id,role_id,menu_id,company_id) select @rownum:=@rownum+1,400000001,id,"+company.getId()+" from (SELECT @rownum:=0) r,sys_menu");
        sqls.add("INSERT INTO `sys_user_role_"+customerId+"`(`id`, `user_id`, `role_id`,company_id) VALUES (1, "+user.getId()+", 400000001,"+company.getId()+")");
        sqls.add("update sys_user set company_id = "+company.getId()+" where customer_id = '"+customerId+"'");

        for( int i=0 ; i<sqls.size() ; i++ ){
            schemaMapper.executeUpdate(sqls.get(i));
        }

        //store
        Store store = new Store();
        store.setDefaultStone(1);
        store.setCompanyId(company.getId());
        store.setName("默认仓库");
        store.setLoginUserId(user.getId());
        store.setCustomerId(customerId);
        ServiceUtils.init_entity_insert(store);
        stoneMapper.insert(store);

        //
    }


    //后台定时清理数据库垃圾空间
    private void clearTable(String tableName,String customerId){
        String sql = "truncate table "+tableName+"_"+customerId;
        this.executeSql(sql);
    }

    private void table( String tableName,Field[] fields,String customerId,int type ){
        if( !isPlatTable(tableName) ){
            tableName += "_"+customerId;
        }

        if( init_type != type ){
            String dropTableSql = "DROP TABLE IF EXISTS "+tableName;
            this.executeSql(dropTableSql);
        }

        StringBuilder sb = new StringBuilder("create table "+tableName+"(");
        sb.append("id bigint NOT NULL,");
        List<String> sqls = Lists.newArrayList();
        for(Field field:fields){
            Transient aTransient = field.getAnnotation(Transient.class);
            if(aTransient!=null || field.getName().equals("id") ){
                continue;
            }


            Column column = field.getAnnotation(Column.class);
            String dbType = getColumnType(field,column);

            sb.append(" "+toColumnName(field.getName())+" "+dbType+" NOT NULL,");

            if( column!=null && column.unique() ){
                sqls.add("ALTER TABLE "+tableName+" ADD UNIQUE ("+toColumnName(field.getName())+")");
            }

            sb.append("\r\n");
        }

        sb.append("PRIMARY KEY (id) )");
        this.executeSql(sb.toString());
        for( String sql : sqls ){
            this.executeSql(sql);
        }
    }

    public void resetCost(SchemaVo schemaVo){

    }

    private void executeSql(String sql){
        logger.info(sql);
        schemaMapper.executeUpdate(sql);
    }

    private static boolean isPlatTable(String tableName){
        for( String value : PLAT_TABLES ){
            if( value.equals(tableName) ){
                return true;
            }
        }
        return false;
    }

    private  static String getColumnType(Field field,Column column){
        String fieldName = field.getName();
        String propertyTypeName = field.getType().getSimpleName();
        String dbType = null;
        if( column != null && StringUtils.isNotEmpty(column.columnDefinition()) ){
            dbType = column.columnDefinition();
        }else{
            if( fieldName.equals("status") ){
                dbType = "TINYINT";
            }else if( propertyTypeName.equals("Long") ){
                dbType = "BIGINT";
            }else if( propertyTypeName.equals("String") ){
                dbType = "CHAR";
            }else if( propertyTypeName.equals("Integer") ){
                dbType = "INT";
            }

            if(!isFixField(field.getName())){
                dbType += "("+column.length()+")";
            }
        }
        return dbType;
    }

    private static boolean isFixField(String fieldName){
        if( fieldName.equals("status") || fieldName.equals("id") || fieldName.equals("customerId") || fieldName.equals("createUserId") || fieldName.equals("createTime") || fieldName.equals("updateUserId")|| fieldName.equals("updateTime")){
            return true;
        }else{
            return false;
        }
    }

    private static String toColumnName(String propertyName){

        char[] a = propertyName.toCharArray();
        List<Character> b = Lists.newArrayList();

        for( int i=0 ; i<a.length ; i++ ){
            if( Character.isLowerCase(a[i]) || Character.isDigit(a[i]) ){
                b.add(a[i]);
            }else{
                b.add('_');
                b.add(Character.toLowerCase(a[i]));
            }
        }

        char[] c = new char[b.size()];
        for( int i=0 ; i<c.length ; i++ ){
            c[i] = b.get(i);
        }

        return new String(c);
    }
}
